﻿/*
    This file is part of nDoctor Light

    NDoctor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    NDoctor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with NDoctor.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Probel.Ldoc.Dal.Configuration
{
    using System;
    using System.ComponentModel.Composition;
    using System.IO;

    using FluentNHibernate.Cfg;
    using FluentNHibernate.Cfg.Db;

    using log4net;

    using NHibernate;
    using NHibernate.Tool.hbm2ddl;

    using Probel.Ldoc.Dal.Exceptions;

    using NHConfiguration = NHibernate.Cfg.Configuration;

    [Export(typeof(IDalManager))]
    public class DalManager : IDalManager
    {
        #region Fields

        public static bool isConfigured;

        private static readonly ILog Logger = LogManager.GetLogger(typeof(DalManager));

        private static ISessionFactory sessionFactory;

        #endregion Fields

        #region Constructors

        static DalManager()
        {
            new AutoMapperMapping().Configure();
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets the nHibernate's session factory.
        /// </summary>
        public static ISessionFactory SessionFactory
        {
            get
            {
                if (sessionFactory == null) throw new NullSessionFactoryException();
                else return sessionFactory;
            }
        }

        private static NHConfiguration Configuration
        {
            get;
            set;
        }

        private Action<NHConfiguration> ConfigurationSetup
        {
            get;
            set;
        }

        private IPersistenceConfigurer MySQLiteConfiguration
        {
            get;
            set;
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Configures the database using the specified file.
        /// </summary>
        /// <param name="fileName">Name of the file (path to the SQLite db).</param>
        /// <param name="createFreshDb">if set to <c>true</c> creates a fresh db. Otherwise uses the already existing database</param>
        /// <returns>A way to have a small fluent interface</returns>
        public DalManager ConfigureUsingFile(string fileName, bool createFreshDb = false)
        {
            if (isConfigured) { throw new ConfigurationException(); }
            if (!File.Exists(fileName)) { createFreshDb = true; }

            if (createFreshDb)
            {
                this.ConfigurationSetup = config =>
                {
                    Configuration = config;

                    // delete the existing db on each run
                    if (File.Exists(fileName))
                    {
                        File.Delete(fileName); //TODO make a backup instead of delete
                        Logger.WarnFormat("Deleting existing database '{0}'", fileName);
                    }
                    else { Logger.DebugFormat("The database '{0}' doesn't exist, creating a new file", fileName); }

                    // this NHibernate tool takes a configuration (with mapping info in)
                    // and exports a database schema from it
                    new SchemaExport(config)
                        .Create(false, true);
                };
            }
            else
            {
                this.ConfigurationSetup = config =>
                {
                    Configuration = config;
                    new SchemaUpdate(config)
                        .Execute(false, true);
                };
            }

            this.MySQLiteConfiguration
                = SQLiteConfiguration
                .Standard
                .UsingFile(fileName);

            this.BuildSessionFactory();
            return this;
        }

        private void BuildSessionFactory()
        {
            sessionFactory = Fluently
                .Configure()
                .Database(this.MySQLiteConfiguration)
                .Mappings(m =>
                {
                    m.AutoMappings
                        .Add(new MyAutoPersistenceModel());
                })
                .ExposeConfiguration(ConfigurationSetup)
                .BuildSessionFactory();

            isConfigured = true;
        }

        #endregion Methods
    }
}